

#include "Base.h"

#include "Viewport.h"
#include "Maths\Matrix4.h"
#include "Maths\Vector3.h"

#include "Memory/DebugNew.h"


namespace CasaEngine
{
	/**
	 * 
	 */
	Viewport::Viewport()
	{
		m_iX = 0;
		m_iY = 0;
		m_iWidth = 1;
		m_iHeight = 1;
		m_fminDepth = 0.0f;
		m_fmaxDepth = 1.0f;

		ComputeAspectRatio();
	}

	/**
	 * 
	 */
	Viewport::Viewport(int x, int y, int width, int height)
	{
		m_iX = x;
		m_iY = y;
		m_iWidth = width;
		m_iHeight = height;
		m_fminDepth = 0.0f;
		m_fmaxDepth = 1.0f;

		ComputeAspectRatio();
	}

	/**
	 * 
	 */
	const Viewport& Viewport::operator=( const Viewport& rsh_ )
	{
		m_iX = rsh_.m_iX;
		m_iY = rsh_.m_iY;
		m_iWidth = rsh_.m_iWidth;
		m_iHeight = rsh_.m_iHeight;
		m_fminDepth = rsh_.m_fminDepth;
		m_fmaxDepth = rsh_.m_fmaxDepth;
		m_fAspectRatio = rsh_.m_fAspectRatio;

		return *this;
	}

	/**
	 * 
	 */
	Viewport::~Viewport()
	{
	}

	/**
	 * 
	 */
	int Viewport::X() const { return m_iX; }

	/**
	 * 
	 */
	void Viewport::X(int val) { m_iX = val; }

	/**
	 * 
	 */
	int Viewport::Y() const { return m_iY; }

	/**
	 * 
	 */
	void Viewport::Y(int val) { m_iY = val; }

	/**
	 * 
	 */
	int Viewport::Width() const 
	{ 
		return m_iWidth; 
	}

	/**
	 * 
	 */
	void Viewport::Width(int val)
	{ 
		m_iWidth = val; 
		ComputeAspectRatio();
	}

	/**
	 * 
	 */
	int Viewport::Height() const 
	{ 
		return m_iHeight;
	}

	/**
	 * 
	 */
	void Viewport::Height(int val)
	{ 
		m_iHeight = val; 
		ComputeAspectRatio();
	}

	/**
	 * 
	 */
	float Viewport::MinDepth() const 
	{ 
		return m_fminDepth; 
	}

	/**
	 * 
	 */
	void Viewport::MinDepth(float val) 
	{ 
		m_fminDepth = val; 
	}

	/**
	 * 
	 */
	float Viewport::MaxDepth() const 
	{ 
		return m_fmaxDepth; 
	}

	/**
	 * 
	 */
	void Viewport::MaxDepth(float val) 
	{ 
		m_fmaxDepth = val; 
	}

	/**
	 * 
	 */
	float Viewport::AspectRatio() const 
	{ 
		return m_fAspectRatio; 
	}

	/**
	 * 
	 */
	void Viewport::ComputeAspectRatio() 
	{ 
		if (m_iHeight != 0)
		{
			m_fAspectRatio = (float) m_iWidth / (float)m_iHeight;
		}
		else
		{
			m_fAspectRatio = 0.0f; 
		}		
	}

	/**
	 * 
	 */
	bool WithinEpsilon(float a, float b)
	{
		float num = a - b;
		return ((-1.401298E-45f <= num) && (num <= 1.401298E-45f));
	}

	/**
	 * 
	 */
	Vector3F Viewport::Project(Vector3F source, Matrix4 projection, Matrix4 view, Matrix4 world)
	{
		Matrix4 mat = world * view * projection; // Matrix4::Multiply(Matrix4.Multiply(world, view), projection);		
		Vector3F vector;
		mat.Transform(source, vector); // Vector3F::Transform(source, mat);
		float a = (((source.x * mat.a14) + (source.y * mat.a24)) + (source.z * mat.a34)) + mat.a44;
		if (!WithinEpsilon(a, 1.0f))
		{
			vector = (Vector3F) (vector / a);
		}
		vector.x = (((vector.x + 1.0f) * 0.5f) * m_iWidth) + m_iX;
		vector.y = (((-vector.y + 1.0f) * 0.5f) * m_iHeight) + m_iY;
		vector.z = (vector.z * (m_fmaxDepth - m_fminDepth)) + m_fminDepth;
		return vector;
	}

	/**
	 * 
	 */
	Vector3F Viewport::Unproject(Vector3F source, Matrix4 projection, Matrix4 view, Matrix4 world)
	{
		Matrix4 mat = (world * view * projection).Invert(); //Matrix4.Invert(Matrix4.Multiply(Matrix4.Multiply(world, view), projection));
		source.x = (((source.x - m_iX) / ((float) m_iWidth)) * 2.0f) - 1.0f;
		source.y = -((((source.y - m_iY) / ((float) m_iHeight)) * 2.0f) - 1.0f);
		source.z = (source.z - m_fminDepth) / (m_fmaxDepth - m_fminDepth);
		Vector3F vector;
		mat.Transform(source, vector); // Vector3F.Transform(source, mat);
		float a = (((source.x * mat.a14) + (source.y * mat.a24)) + (source.z * mat.a34)) + mat.a44;
		if (!WithinEpsilon(a, 1.0f))
		{
			vector = (Vector3F) (vector / a);
		}
		return vector;
	}

	
} // namespace CasaEngine
